package org.javaforever.oville.operator;

import java.util.ArrayList;
import java.util.List;

import org.javaforever.oville.NarrowWord;
import org.javaforever.oville.Quad;
import org.javaforever.oville.TextWord;
import org.javaforever.oville.ValidateException;
import org.javaforever.oville.Word;

public class Project {	
	public static List<Word> halfProject(Word encoded) throws ValidateException{
		List<Word> results = new ArrayList<>();
		long qCounts = countQ(encoded);
		System.out.println("JerryDebug:"+qCounts+":"+Math.pow(qCounts, 2));
		long ceiling = (long) (Math.round(Math.pow(2, qCounts))- 0.1);
		for (long i=0;i<=ceiling;i++) {
			results.add(encoded);
		}
		List<Word> finalresults = new ArrayList<>();
		for (int i=0;i<results.size();i++) {
			finalresults.add(replaceQsWithVals(results.get(i),i));
		}
		return finalresults;
	}
	
	public static List<NarrowWord> project(Word encoded, char pChar) throws ValidateException{
		List<Word> results = halfProject(encoded);
		List<NarrowWord> retVals = new ArrayList<>();
		for (Word w : results) {
			TextWord tw = w.toTextWord("01Q"+pChar);
			retVals.add(tw.toNarrowWord("01"));
		}
		return retVals;
	}
	
	public static Word quadListToWord(List<Quad> list) throws ValidateException{
		Word w = new Word();
		byte [] bb = new byte[8];
		for (int i=0;i<8;i++) {
			byte currentBb = Word.fourQuadToByte(list.subList(i*4, i*4+4));
			bb [7-i] = currentBb;
		}
		w.setWord(bb);
		return w;
	}

	private static List<List<Quad>> appendExpandWords(List<List<Quad>> quads, Quad q, int pos) {
		List<List<Quad>> results = new ArrayList<>();
		for (List<Quad> qds:quads) {
			if (pos>0) qds = qds.subList(0,pos);
			qds.add(new Quad("0"));
			results.add(qds);
		}
		for (List<Quad> qds:quads) {
			if (pos>0) qds = qds.subList(0,pos);
			qds.add(new Quad("1"));
			results.add(qds);
		}
		return results;
	}

	private static List<List<Quad>> appendWords(List<List<Quad>> quads, Quad q, int pos) {
		List<List<Quad>> results = new ArrayList<>();
		for (List<Quad> qds:quads) {
			if (pos>0) qds = qds.subList(0,pos);
			qds.add(q);
			results.add(qds);
		}
		return results;
	}

	private static List<Quad> getQuads(Word encoded) throws ValidateException{
		String quadStr = encoded.toQuadStr();
		List<Quad> quads = new ArrayList<>();
		for (int i=0;i<quadStr.length();i++) {
			quads.add(new Quad(quadStr.charAt(i)));
		}
		return quads;
	}
	
	private static Quad [] getQuadsArr(Word encoded) throws ValidateException{
		String quadStr = encoded.toQuadStr();
		List<Quad> quads = new ArrayList<>();
		for (int i=0;i<quadStr.length();i++) {
			quads.add(new Quad(quadStr.charAt(i)));
		}
		return quads.toArray(new Quad[quads.size()]);
	}
	
	private static int countQandP(Quad [] quads) throws ValidateException{
		int count = 0;
		for (Quad q:quads) {
			if ("Q".equals(q.toQuadStr())||"P".equals(q.toQuadStr())) {
				count ++;
			}
		}
		return count;
	}
	
	private static int countQ(Quad [] quads) throws ValidateException{
		int count = 0;
		for (Quad q:quads) {
			if ("Q".equals(q.toQuadStr())) {
				count ++;
			}
		}
		return count;
	}
	
	private static int countQ(Word word) throws ValidateException{
		String qwStr = word.toQuadStr();
		int count = 0;
		for (int i=0;i<qwStr.length();i++) {
			if ('Q' == qwStr.charAt(i)) {
				count ++;
			}
		}
		return count;
	}
	
	private static String longValsToString(long vals, int qCounts) throws ValidateException {
		String retVals = Long.toBinaryString(vals);
		if (retVals.length()>qCounts) throw new ValidateException("Unmatch Qs and vals");
		else if (retVals.length()<qCounts) {
			int ceiling = qCounts - retVals.length();
			for (int i=0;i< ceiling;i++) {
				retVals = "0" + retVals;
			}		
		}
		return retVals;
	}
	
	private static Word replaceQsWithVals(Word target, long vals) throws ValidateException {		
		int qCounts = countQ(target);		
		StringBuilder targetSb = new StringBuilder(target.toQuadStr());
		String valsStr = longValsToString(vals,qCounts);
		System.out.println("JerryDebug:"+target.toQuadStr()+":"+vals+":"+qCounts+":"+valsStr);
		if (qCounts != valsStr.length()) throw new ValidateException("Unmatch Qs and vals");
		int qpos =0;
		for (int i=0;i<qCounts;i++) {
			for (int j=0;j<targetSb.length();j++) {
				if ('Q'== targetSb.charAt(j)) {
					targetSb.replace(j, j+1, ""+valsStr.charAt(qpos));
					qpos++;
				}
			}			
		}
		System.out.println("JerryDebug:"+Word.fromQuadStr(targetSb.toString()));
		return Word.fromQuadStr(targetSb.toString());
	}

	public static void main(String [] args){
		try {
			Word w = Word.fromQuadStr("Q101010101010101010101010101PQQQ");
			System.out.println(w.toQuadStr());
			List<Word> words = halfProject(w);
			System.out.println(words.size());
			for (Word ww:words) {
				System.out.println(ww.toQuadStr());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
